<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
  <head>
    <title>BrowserBox - Legacy Client (IE4)</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <style type="text/css">
      html, body { height: 100%; overflow: hidden; }
      body { font-family: "MS Sans Serif", "Tahoma", "Verdana", sans-serif; font-size: 12px; margin: 0; padding: 0; background-color: ButtonFace; color: ButtonText; overflow: hidden; }
      #main-layout { width: 100%; height: 100%; border-spacing: 0; }
      #tab-bar { background-color: ButtonFace; padding: 3px 2px 0 2px; border-bottom: 2px solid darkgray; white-space: nowrap; overflow: hidden; }
      .tab { display: inline-block; padding: 4px 8px; border: 1px solid ButtonFace; border-bottom: none; margin-right: 2px; cursor: pointer; background-color: darkgray; position: relative; top: 1px; }
      .tab-active { background-color: ButtonFace; border: 1px solid darkgray; border-bottom: 1px solid ButtonFace; font-weight: bold; }
      #omni-box { background-color: ButtonFace; padding: 5px; border-bottom: 2px outset silver; }
      .omni-button { font-size: 12px; width: 60px; margin-right: 5px; }
      #url-input { font-size: 12px; width: 60%; }
      #status-bar { padding: 3px; border-top: 1px solid darkgray; font-size: 10px; color: black; }
      #content-pane { position: relative; background-color: #000; overflow: hidden; padding: 0; border-top: 1px solid darkgray; }
      #remote-screen {
        position: absolute; display: block; top: 0; left: 0;
        width: 100%; height: 100%; background-color: #fff; cursor: crosshair;
      }
      #new-tab-button { display: inline-block; padding: 4px 6px; margin-right: 4px; border: 1px solid darkgray; background-color: silver; cursor: pointer; font-weight: bold; position: relative; top: 1px; }
      .close-tab-button { font-family: "Webdings", sans-serif; font-size: 12px; padding-left: 10px; color: darkgray; cursor: pointer; }
      /* Hidden container for IE4 JSONP iframes (optional) */
      #jsonp-frames { width:0; height:0; overflow:hidden; }
    </style>
    <script type="text/javascript" language="JavaScript">
      function initBrowserBoxLegacyClient() {
        /* ---------- Minimal polyfills ---------- */
        if (!Array.prototype.shift) {
          Array.prototype.shift = function () {
            var len = this.length >>> 0;
            if (!len) { this.length = 0; return void 0; }
            var first = this[0], i;
            for (i = 1; i < len; i++) this[i - 1] = this[i];
            this.length = len - 1;
            return first;
          };
        }
        if (!Array.prototype.unshift) {
          Array.prototype.unshift = function () {
            var add = arguments.length, len = this.length >>> 0, i;
            if (!add) return len;
            for (i = len - 1; i >= 0; i--) this[i + add] = this[i];
            for (i = 0; i < add; i++) this[i] = arguments[i];
            this.length = len + add;
            return this.length;
          };
        }
        if (!window.encodeURIComponent) { window.encodeURIComponent = function (s) { return escape(s).split('+').join('%2B'); }; }
        if (!window.decodeURIComponent) { window.decodeURIComponent = function (s) { return unescape(s); }; }

        /* Detect IE4: has document.all but no getElementById */
        var IS_IE4 = true || (!!document.all && !document.getElementById);

        function getEl(id) {
          if (document.getElementById) return document.getElementById(id);
          if (document.all)            return document.all[id];
          return null;
        }

        /* ---------- Minimal JSON.stringify ---------- */
        if (!window.JSON) { window.JSON = {}; }
        if (!JSON.stringify) {
          (function () {
            function escStr(s) {
              var out = '', i, c, ch;
              for (i = 0; i < s.length; i++) {
                c = s.charCodeAt(i); ch = s.charAt(i);
                if (ch == '"' || ch == '\\') out += '\\' + ch;
                else if (c < 32) {
                  if (ch == '\b') out += '\\b';
                  else if (ch == '\f') out += '\\f';
                  else if (ch == '\n') out += '\\n';
                  else if (ch == '\r') out += '\\r';
                  else if (ch == '\t') out += '\\t';
                  else { var h = c.toString(16); while (h.length < 4) h = '0' + h; out += '\\u' + h; }
                } else out += ch;
              }
              return '"' + out + '"';
            }
            function isArr(a) { return a && (a.constructor == Array || (typeof a.length == 'number' && typeof a.splice == 'function')); }
            function isDate(d) { return d && typeof d.getUTCFullYear == 'function'; }
            function s(v, stack) {
              var t = typeof v, i, j;
              if (v === null) return 'null';
              if (t == 'string') return escStr(v);
              if (t == 'number') return (v - v == 0) ? '' + v : 'null';
              if (t == 'boolean') return v ? 'true' : 'false';
              stack = stack || [];
              for (i = 0; i < stack.length; i++) if (stack[i] == v) return escStr('[Circular]');
              if (isDate(v)) return escStr(v.toUTCString ? v.toUTCString() : '' + v);
              if (isArr(v)) {
                stack[stack.length] = v;
                var out = [];
                for (j = 0; j < v.length; j++) {
                  var sv = s(v[j], stack);
                  out[out.length] = (typeof v[j] == 'undefined' || typeof v[j] == 'function') ? 'null' : sv;
                }
                stack.length = stack.length - 1;
                return '[' + out.join(',') + ']';
              }
              if (t == 'object') {
                stack[stack.length] = v;
                var parts = [], k, hop = Object.prototype.hasOwnProperty;
                for (k in v) { if (hop ? hop.call(v, k) : true) {
                  var v2 = v[k], vt = typeof v2;
                  if (vt != 'undefined' && vt != 'function') parts[parts.length] = escStr(k) + ':' + s(v2, stack);
                }}
                stack.length = stack.length - 1;
                return '{' + parts.join(',') + '}';
              }
              return 'null';
            }
            JSON.stringify = function (x) { return s(x, []); };
          })();
        }

        function _decodeQS(s){ return decodeURIComponent(String(s).replace(/\+/g,'%20')); }
        function getQueryParam(name){
          var q=(location&&location.search)?String(location.search).replace(/^\?/,''):'';
          if(!q)return '';
          var parts=q.split('&'),i,kv,rest;
          for(i=0;i<parts.length;i++){ kv=parts[i].split('='); if(_decodeQS(kv[0])==name){ rest=kv.slice(1).join('='); return _decodeQS(rest); } }
          return '';
        }

        /* ---------- Config / state ---------- */
        var KEY_BATCH_INTERVAL_MS = 1000;
        var MAX_KEYS_PER_BATCH    = 24;
        var keyBuffer = [];
        var keyBatchInterval = null;

        var VIEWPORT_SYNC_DELAY_MS = 750;
        var isNetworkError = false;

        var API_BASE_PATH = '/api/vwin';
        if (location && location.origin) API_BASE_PATH = location.origin + API_BASE_PATH;

        var POLLING_INTERVAL_MS = 1000;
        var TAB_REFRESH_RATE_MS = 4000;
        var RESIZE_THROTTLE_MS  = 1000;

        var NUDGE_MIN_MS = 400;
        var _lastNudgeAt = 0, _nudgeImgBusy = false, _nudgeImg = null;

        var tabPollingInterval = null;

        var activeTabId = null;
        var isConnected = false;
        var resizeTimeout = null;
        var lastKnownFrameTimestamp = 0;
        var isUrlBarFocused = false;
        var _gettingFrame = false;

        /* ---------- Elements ---------- */
        var contentPane   = getEl('content-pane');
        var connectButton = getEl('connect-button');
        var tokenInput    = getEl('token-input');
        var tabBar        = getEl('tab-bar');
        var backButton    = getEl('back-button');
        var forwardButton = getEl('forward-button');
        var urlInput      = getEl('url-input');
        var goButton      = getEl('go-button');
        var remoteScreen  = getEl('remote-screen');
        var statusBar     = getEl('status-bar');

        var sessionToken = getQueryParam('token') || '';
        if (tokenInput) tokenInput.value = sessionToken;

        /* ---------- JSONP (IE4-proof) ---------- */
        var _jsonpSeq = 0;
        var __jsonpCallbacks = {}; // cbName -> { onOK, timeoutId, frame }

        function addParam(u,k,v){ return u + (u.indexOf('?')>=0?'&':'?') + k + '=' + v; }

        // Parent dispatcher that request-specific callbacks will forward to
        if (!window.__jsonpCallbacks) window.__jsonpCallbacks = __jsonpCallbacks;

        if (!window.__jsonpDispatch) {
          if (window.execScript) {
            window.execScript(
              'function __jsonpDispatch(cbName, data) {' +
                'var e = window.__jsonpCallbacks && window.__jsonpCallbacks[cbName];' +
                'if (e && e.timeoutId) { clearTimeout(e.timeoutId); } ' +
                'if (e) {' +
                  'window.__jsonpCallbacks[cbName] = null;' +
                  'if (e.onOK) e.onOK(data); ' +
                '}' +
              '}',
              'JScript'
            );
          } else {
            // Non-IE4 path (harmless elsewhere).
            window.__jsonpDispatch = function (cbName, data) {
              var e = window.__jsonpCallbacks && window.__jsonpCallbacks[cbName];
              if (e && e.timeoutId) { clearTimeout(e.timeoutId); }
              if (e) {
                window.__jsonpCallbacks[cbName] = null;
                if (e.onOK) e.onOK(data); 
              }
            };
          }
        }

        function jsonpViaIframe(fullUrl, cbName, onOK, onTimeout) {
          // Create hidden iframe
          var iframe = getEl('iframe-buddy');
          if (!iframe) {
            // last-resort: overwrite document (undesirable) — but IE4 has iframes, so we shouldn't hit this
            if (onTimeout) onTimeout();
            return;
          }
          iframe.style.width = '0px';
          iframe.style.height = '0px';
          iframe.style.border = 0;
          iframe.style.visibility = 'hidden';
          iframe.setAttribute ? iframe.setAttribute('frameBorder','0') : (iframe.frameBorder = 0);

          // Keep a reference for cleanup
          __jsonpCallbacks[cbName] = { onOK: onOK, timeoutId: null, frame: iframe };

          // Get the frame's document
          var ifrWin = iframe.contentWindow || iframe.window || (window.frames ? window.frames[iframe.name] : null);
          var d = ifrWin ? ifrWin.document : (iframe.document || null);
          if (!d) { if (onTimeout) onTimeout(); return; }

          var page =
            '<html><head>' +
              '<scr' + 'ipt type="text/javascript" language="JavaScript">' +
                'function __bb_send(d){' +
                  'var P=null; P=parent;' +
                  'if(P && P.__jsonpDispatch){ P.__jsonpDispatch("' + cbName + '", d); return; } ' +
                  'if(P && P.window && P.window.__jsonpDispatch){ P.window.__jsonpDispatch("' + cbName + '", d); return; } ' +
                  'if(top && top.__jsonpDispatch){ top.__jsonpDispatch("' + cbName + '", d); return; }' +
                '}' +
                'function ' + cbName + '(d){ __bb_send(d); }' +
                // also export on window for servers that do `window.cb(...)`
                'window["' + cbName + '"] = ' + cbName + '; ' +
              '</scr' + 'ipt>' +
              '<scr' + 'ipt type="text/javascript" language="JavaScript" src="' + fullUrl + '"></scr' + 'ipt>' +
            '</head><body></body></html>';

          //alert(page);
          d.open(); d.write(page); d.close();

          // timeout fallback
          __jsonpCallbacks[cbName].timeoutId = setTimeout(function(){
            var entry = __jsonpCallbacks[cbName];
            if (!entry) return;
            __jsonpCallbacks[cbName] = null;
            if (onTimeout) onTimeout();
          }, 3000);
        }

        function jsonp(fullUrl, onOK, onTimeout) {
          if (!document.body) { setTimeout(function(){ jsonp(fullUrl, onOK, onTimeout); }, 50); return; }

          // Build callback name & full URL (include token + jsonp params)
          var cbName = '__bbcb' + (++_jsonpSeq);
          var url = fullUrl;
          if (url.indexOf('session_token=') < 0 && sessionToken) url = addParam(url, 'session_token', encodeURIComponent(sessionToken));
          url = addParam(url, 'jsonp', '1');
          url = addParam(url, 'callback', cbName);
          url = addParam(url, 'ran', '' + Math.random());

          if (IS_IE4) {
            // IE4 path: IFRAME + write
            jsonpViaIframe(url, cbName, onOK, onTimeout);
            return;
          }

          // IE5+/modern path: dynamic <script>
          __jsonpCallbacks[cbName] = { onOK: onOK, timeoutId: null, frame: null };
          // Install a global stub (so server can call cbName directly)
          var stub = 'window.' + cbName + ' = function ' + cbName + '(d){__jsonpDispatch("' + cbName + '", d);}';
          if (window.execScript) window.execScript(stub, 'JavaScript'); else eval(stub);
          window[cbName] = function(d){__jsonpDispatch(cbName, d);};

          var s = document.createElement ? document.createElement('script') : null;
          if (s && document.body && document.body.appendChild) {
            s.type = 'text/javascript';
            s.src  = url;
            document.body.appendChild(s);
          } else {
            // Ultra fallback
            document.write('<scr'+'ipt type="text/javascript" src="' + url + '"></scr'+'ipt>');
          }

          __jsonpCallbacks[cbName].timeoutId = setTimeout(function(){
            if (!__jsonpCallbacks[cbName]) return;
            __jsonpCallbacks[cbName] = null;
            if (onTimeout) onTimeout();
          }, 3000);
        }

        function getJSON(fullUrl, onOK, onErr) {
          jsonp(fullUrl, function (data){ if(onOK) onOK(data); },
                       function ()    { if(onErr) onErr();    });
        }

        /* ---------- UI helpers ---------- */
        function updateStatus(text){ var el=statusBar; if (el) el.innerHTML = 'Status: ' + text; }

        function setControlsEnabled(isEnabled){
          if (backButton)    backButton.disabled    = !isEnabled;
          if (forwardButton) forwardButton.disabled = !isEnabled;
          if (urlInput)      urlInput.disabled      = !isEnabled;
          if (goButton)      goButton.disabled      = !isEnabled;
        }

        function safeClearChildNodes(el){
          if (!el) return;
          if (typeof el.innerHTML != 'undefined') { el.innerHTML = ''; return; }
          while (el.firstChild) { el.removeChild(el.firstChild); }
        }

        function handleReconnection(){
          if (isNetworkError) {
            isNetworkError = false;
            updateStatus('Connection re-established. Syncing viewport...');
            setTimeout(sendViewportSize, VIEWPORT_SYNC_DELAY_MS);
          }
        }

        function maybeNudgeCapture(){
          var now = (new Date()).getTime();
          if (_nudgeImgBusy) return;
          if (now - _lastNudgeAt < NUDGE_MIN_MS) return;
          _lastNudgeAt = now; _nudgeImgBusy = true;
          if (!_nudgeImg) _nudgeImg = new Image();
          _nudgeImg.onload  = function(){ _nudgeImgBusy = false; };
          _nudgeImg.onerror = function(){ _nudgeImgBusy = false; };
          _nudgeImg.src = API_BASE_PATH + '/frame?session_token=' + encodeURIComponent(sessionToken) + '&nudge=1&ran=' + Math.random();
        }

        /* ---------- Connect / tabs / frames ---------- */
        function toggleConnection(){
          if (isConnected) {
            isConnected = false;
            stopKeyBatching();
            if (tabPollingInterval) { clearInterval(tabPollingInterval); tabPollingInterval = null; }
            sessionToken = '';
            activeTabId = null;
            lastKnownFrameTimestamp = 0;
            if (connectButton) connectButton.innerHTML = 'Connect';
            updateStatus('Disconnected.');
            setControlsEnabled(false);
            safeClearChildNodes(tabBar);
            if (urlInput) urlInput.value = '';
            return;
          }

          sessionToken = tokenInput ? tokenInput.value : '';
          if (!sessionToken) { alert('Please enter a session token.'); return; }

          updateStatus('Connecting...');
          isConnected = true; // don’t block on JSONP ack

          connect();          // fire-and-forget
          sendViewportSize();
          updateTabs();
          pollForNextFrame();
          startKeyBatching();

          if (tabPollingInterval) clearInterval(tabPollingInterval);
          tabPollingInterval = setInterval(updateTabs, TAB_REFRESH_RATE_MS);
          if (connectButton) connectButton.innerHTML = 'Disconnect';
          setControlsEnabled(true);
          setTimeout(function(){ handleReconnection(); }, 500);
        }

        function connect(){
          var url = API_BASE_PATH + '/connect?session_token=' + encodeURIComponent(sessionToken) + '&ran=' + Math.random();
          getJSON(url, function(){ updateStatus('Connected (ack).'); },
                       function(){ updateStatus('Connected (no ack).'); });
        }

        function updateTabs(){
          if (!isConnected) return;
          var url = API_BASE_PATH + '/tabs?session_token=' + encodeURIComponent(sessionToken) + '&ran=' + Math.random();
          getJSON(url, function(data){
            handleReconnection();
            if (!data) return;
            var tabs = data.tabs || [];
            renderTabs(tabs, data.activeTarget);
            activeTabId = data.activeTarget;
            if (!isUrlBarFocused && tabs.length && urlInput) {
              var i; for (i=0;i<tabs.length;i++){
                if (tabs[i].targetId == activeTabId) { urlInput.value = tabs[i].url || ''; break; }
              }
            }
          }, function(){ /* next tick will retry */ });
        }

        function pollForNextFrame(){
          if (!isConnected || !activeTabId) { setTimeout(pollForNextFrame, POLLING_INTERVAL_MS); return; }
          var url = API_BASE_PATH + '/frame-status?session_token=' + encodeURIComponent(sessionToken) +
                    '&targetId=' + encodeURIComponent(activeTabId) +
                    '&last_known_ts=' + lastKnownFrameTimestamp +
                    '&ran=' + Math.random();
          getJSON(url, function(data){
            handleReconnection();
            if (data && data.fresh) {
              lastKnownFrameTimestamp = data.timestamp;
              updateScreenImage();
            } else {
              maybeNudgeCapture();
              setTimeout(pollForNextFrame, POLLING_INTERVAL_MS);
            }
          }, function(){ setTimeout(pollForNextFrame, POLLING_INTERVAL_MS * 2); });
        }

        function updateScreenImage(){
          if (_gettingFrame) return;
          _gettingFrame = true;

          var newSrc = API_BASE_PATH + '/frame?session_token=' + encodeURIComponent(sessionToken) +
                       '&targetId=' + encodeURIComponent(activeTabId || '') +
                       (lastKnownFrameTimestamp ? '&ts=' + lastKnownFrameTimestamp : '') +
                       '&ran=' + Math.random();

          if (IS_IE4) {
            remoteScreen.src = newSrc;
            _gettingFrame = false;
            setTimeout(pollForNextFrame, POLLING_INTERVAL_MS);
            return;
          }

          var parent = contentPane; if (!parent) { _gettingFrame = false; return; }
          var newImage = document.createElement('img');
          newImage.onload = function(){
            var oldImage = getEl('remote-screen');
            newImage.id = 'remote-screen';
            newImage.alt = oldImage.alt;
            newImage.style.cssText = oldImage.style.cssText;
            newImage.onclick = handleScreenClick;
            parent.appendChild(newImage);
            if (oldImage) { oldImage.onload = oldImage.onerror = null; parent.removeChild(oldImage); }
            remoteScreen = newImage;
            remoteScreen.alt = "BrowserBox View";
            updateStatus('Frame ' + (lastKnownFrameTimestamp || '?') + ' loaded.');
            _gettingFrame = false;
            pollForNextFrame();
          };
          newImage.onerror = function(){
            if (!isNetworkError) { isNetworkError = true; updateStatus('Connection error. Retrying...'); }
            _gettingFrame = false;
            setTimeout(pollForNextFrame, POLLING_INTERVAL_MS);
          };
          newImage.src = newSrc;
        }

        /* ---------- Events ---------- */
        var _evtPool = [];
        var _evtPoolMax = 8;

        function sendEvent(params){
          if (!isConnected) return;
          var url = API_BASE_PATH + '/event?session_token=' + encodeURIComponent(sessionToken) +
                    (activeTabId ? '&targetId=' + encodeURIComponent(activeTabId) : '') +
                    '&' + params +
                    '&cb=' + ((new Date()).getTime() % 1000000000) + '&ran=' + Math.random();
          if (window.Image) {
            var img = new Image();
            _evtPool[_evtPool.length] = img;
            if (_evtPool.length > _evtPoolMax) { _evtPool.shift(); }
            img.src = url;
          } else {
            var f = document.all ? document.all['evt-iframe'] : null;
            if (!f && document.body && document.body.insertAdjacentHTML) {
              document.body.insertAdjacentHTML('beforeEnd','<iframe id="evt-iframe" width="0" height="0" frameborder="0"></iframe>');
              f = document.all ? document.all['evt-iframe'] : null;
            }
            if (f) { f.src = url; }
          }
        }

        function sendViewportSize(){
          var w = contentPane.clientWidth  || contentPane.offsetWidth  || remoteScreen.offsetWidth  || 0;
          var h = contentPane.clientHeight || contentPane.offsetHeight || remoteScreen.offsetHeight || 0;
          remoteScreen.width  = w;
          remoteScreen.height = h;
          sendEvent('type=resize&width=' + w + '&height=' + h);
        }

        function throttledResizeHandler(){
          remoteScreen.style.display = 'none';
          if (resizeTimeout) { clearTimeout(resizeTimeout); }
          resizeTimeout = setTimeout(doResize, RESIZE_THROTTLE_MS);
        }
        function doResize(){
          remoteScreen.style.display = 'block';
          if (isConnected) sendViewportSize();
        }

        /* ---------- Mouse / keyboard ---------- */
        function getScroll(){
          var d = document.documentElement, b = document.body;
          return { x: (d && d.scrollLeft) || (b && b.scrollLeft) || 0,
                   y: (d && d.scrollTop)  || (b && b.scrollTop)  || 0 };
        }
        function getOffset(el){
          var x=0,y=0; while(el){ x+=el.offsetLeft||0; y+=el.offsetTop||0; el=el.offsetParent; }
          return { x:x, y:y };
        }

        function handleScreenClick(e){
          e = e || window.event;
          var off = getOffset(remoteScreen), scr = getScroll();
          var cx = (e.clientX != null ? e.clientX : 0) + scr.x;
          var cy = (e.clientY != null ? e.clientY : 0) + scr.y;
          var x = (e.offsetX != null) ? e.offsetX : (cx - off.x);
          var y = (e.offsetY != null) ? e.offsetY : (cy - off.y);
          sendEvent('type=mousedown&x=' + x + '&y=' + y);
        }

        function handleMouseWheel(e){
          e = e || window.event;
          var t = e.srcElement; if (!t || t.id != 'remote-screen') return;
          var off = getOffset(remoteScreen), scr = getScroll();
          var cx = (e.clientX != null ? e.clientX : 0) + scr.x;
          var cy = (e.clientY != null ? e.clientY : 0) + scr.y;
          var x = (e.offsetX != null) ? e.offsetX : (cx - off.x);
          var y = (e.offsetY != null) ? e.offsetY : (cy - off.y);
          var delta = (typeof e.wheelDelta != 'undefined') ? e.wheelDelta : 0;
          sendEvent('type=mousewheel&x=' + x + '&y=' + y + '&deltaY=' + delta);
        }

        var nonPrintableKeys = [8,9,13,27,33,34,35,36,37,38,39,40,45,46];

        function indexOf(arr,val){ var i; for(i=0;i<arr.length;i++){ if(arr[i]==val) return i; } return -1; }

        function startKeyBatching(){
          if (keyBatchInterval) { clearInterval(keyBatchInterval); }
          keyBatchInterval = setInterval(sendKeyBatch, KEY_BATCH_INTERVAL_MS);
        }
        function stopKeyBatching(){
          if (keyBatchInterval) { clearInterval(keyBatchInterval); keyBatchInterval = null; }
          keyBuffer = [];
        }
        function sendKeyBatch(){
          if (keyBuffer.length == 0) return;
          var start = 0;
          while (start < keyBuffer.length) {
            var end = start + MAX_KEYS_PER_BATCH;
            var chunk = [], i;
            for (i = start; i < end && i < keyBuffer.length; i++) chunk[chunk.length] = keyBuffer[i];

            var eventsJson = '[';
            var j;
            for (j = 0; j < chunk.length; j++) {
              var keyEvent = chunk[j];
              eventsJson += '{' +
                '"p":"' + (keyEvent.press || '') + '",' +
                '"k":' + (keyEvent.keyCode != null ? keyEvent.keyCode : '""') + ',' +
                '"s":' + (keyEvent.shiftKey ? 1 : 0) + ',' +
                '"c":' + (keyEvent.ctrlKey ? 1 : 0) + ',' +
                '"a":' + (keyEvent.altKey ? 1 : 0) +
              '}';
              if (j < chunk.length - 1) eventsJson += ',';
            }
            eventsJson += ']';

            sendEvent('type=key_batch&events=' + encodeURIComponent(eventsJson));
            start = end;
          }
          keyBuffer = [];
        }

        function handleKeyDown(e){
          if (!isConnected) return;
          e = e || window.event;
          var keyCode = e.keyCode || e.which;

          if (isUrlBarFocused) {
            if (keyCode == 13) { handleNavigate(); e.returnValue = false; }
            return;
          }

          if (indexOf(nonPrintableKeys, keyCode) != -1) {
            keyBuffer[keyBuffer.length] = {
              keyCode: keyCode,
              shiftKey: e.shiftKey,
              ctrlKey: e.ctrlKey,
              altKey: e.altKey
            };
            e.returnValue = false;
          }
        }

        function handleKeyPress(e){
          if (!isConnected || isUrlBarFocused) return;
          e = e || window.event;
          var charCode = e.charCode || e.keyCode;
          keyBuffer[keyBuffer.length] = {
            press: String.fromCharCode(charCode),
            shiftKey: e.shiftKey,
            ctrlKey: e.ctrlKey,
            altKey: e.altKey
          };
        }

        /* ---------- Navigation helpers ---------- */
        function parseURL(u){
          if (!u) return null;
          var s = '' + u;
          if (!/^https?:\/\//i.test(s)) {
            if (/^[a-z0-9.-]+\.[a-z]{2,}/i.test(s)) s = 'https://' + s;
            else return null;
          }
          return { href: s };
        }

        function handleNavigate(){
          var raw = urlInput ? urlInput.value : '';
          if (!raw) return;
          var parsed = parseURL(raw);
          if (!parsed) parsed = parseURL('https://' + raw);
          var href = parsed ? parsed.href : ('https://duckduckgo.com/?q=' + encodeURIComponent(raw));
          sendEvent('type=navigate&url=' + encodeURIComponent(href));
        }

        function handleBack()    { sendEvent('type=back'); }
        function handleForward() { sendEvent('type=forward'); }
        function handleNewTab()  { sendEvent('type=new_tab'); }
        function handleCloseTab(targetIdToClose) { sendEvent('type=close_tab&targetIdToClose=' + targetIdToClose); }

        function renderTabs(tabs, currentActiveId){
          var previousActiveTab = activeTabId;
          safeClearChildNodes(tabBar);

          var newTabBtn = document.createElement('span');
          newTabBtn.id = 'new-tab-button';
          newTabBtn.innerHTML = '+';
          newTabBtn.onclick = handleNewTab;
          tabBar.appendChild(newTabBtn);

          var i;
          for (i = 0; i < tabs.length; i++) {
            var tab = tabs[i];
            var tabEl = document.createElement('span');
            tabEl.className = 'tab' + (tab.targetId == currentActiveId ? ' tab-active' : '');
            var titleText = (tab.title || 'New Tab').substring(0, 20);
            if (typeof tabEl.innerText != 'undefined') tabEl.innerText = titleText; else tabEl.innerHTML = titleText;

            tabEl._tid = tab.targetId;
            tabEl.onclick = function(){
              var targetId = this._tid;
              if (targetId != activeTabId) { activeTabId = targetId; sendEvent('type=switch'); }
            };

            var closeBtn = document.createElement('span');
            closeBtn.className = 'close-tab-button';
            closeBtn.innerHTML = 'r'; /* Webdings 'x' */
            closeBtn._tid = tab.targetId;
            closeBtn.onclick = function(e){
              e = e || window.event;
              if (e && e.cancelBubble != null) e.cancelBubble = true;
              handleCloseTab(this._tid);
            };

            tabEl.appendChild(closeBtn);
            tabBar.appendChild(tabEl);
          }

          if (currentActiveId && currentActiveId != previousActiveTab) {
            lastKnownFrameTimestamp = 0;
          }
        }

        /* ---------- Wire up ---------- */
        if (connectButton) connectButton.onclick = toggleConnection;
        if (remoteScreen)  remoteScreen.onclick  = handleScreenClick;
        if (goButton)      goButton.onclick      = handleNavigate;
        if (backButton)    backButton.onclick    = handleBack;
        if (forwardButton) forwardButton.onclick = handleForward;

        window.onresize       = throttledResizeHandler;
        document.onkeydown    = handleKeyDown;
        document.onkeypress   = handleKeyPress;
        document.onmousewheel = handleMouseWheel;

        if (sessionToken && connectButton && connectButton.click) { connectButton.click(); }
        else if (sessionToken) { toggleConnection(); }
      }
    </script>
  </head>
  <body onload="initBrowserBoxLegacyClient()">
    <table id="main-layout" cellpadding="0" cellspacing="0">
      <tr style="height: 1px; display: none;">
        <td>
          <div style="padding: 5px;">
            Session Token:
            <input type="text" id="token-input" size="20" value="">
            <button id="connect-button">Connect</button>
          </div>
        </td>
      </tr>
      <tr style="height: 25px;"><td><div id="tab-bar"></div></td></tr>
      <tr style="height: 35px;">
        <td>
          <div id="omni-box">
            <button id="back-button" class="omni-button" disabled>Back</button>
            <button id="forward-button" class="omni-button" disabled>Forward</button>
            <input type="text" id="url-input" disabled onfocus="isUrlBarFocused = true;" onblur="isUrlBarFocused = false;">
            <button id="go-button" class="omni-button" disabled>Go</button>
          </div>
        </td>
      </tr>
      <tr style="height: 100%;"><td id="content-pane">
        <img id="remote-screen" src="placeholder.gif" alt="Loading Remote Browser View ..." style="width: 100%; height: 100%;">
      </td></tr>
      <tr style="height: 20px;"><td><div id="status-bar">Status: Disconnected.</div></td></tr>
    </table>

    <div id="jsonp-frames">
      <iframe src="/win9x/start.html" id="iframe-buddy" name="iframe-buddy"></iframe>
    </div>
  </body>
</html>

